﻿using System;
using System.Collections.Generic;

namespace rehot
{
    using rehot.core;
    using SanlieHash;

    /// <summary>
    /// 动态类型
    /// </summary>
    public class HotType : IDisposable
    {
        internal Type _type { get; set; }
        public HotMethods Methods { get; set; }
        public HotEvents Events { get; set; }
        public HotMembers Members { get; set; }
        public HotAttributes Attributes { get; set; }
        public string Name { get; }
        public string Id { get; internal set; }
        public Type SystemType { get { return _type; } }
        public void Dispose()
        {
            this._type = null;
            if (Methods !=null) Methods.Lose();
            Methods = null;
            if (Events != null) Events.Lose();
            Events = null;
            if(Members!=null) Members.Lose();
            Members = null;
            if (Attributes != null) Attributes.Lose();
            Attributes = null;
        }
        public HotType(Type type) : this(type, true) { }
        internal HotType(Type type, bool get_attrs)
        {
            if (type == null) throw new ArgumentNullException("type not found.");
            this._type = type;
            this.Name = this._type.Name;
            this.Id = FastHash.GetSM3("Type#" + Name);
            this.Methods = new HotMethods(this);
            this.Events = new HotEvents(this);
            this.Members = new HotMembers(this);
            if (get_attrs) this.Attributes = new HotAttributes(this);
        }
        public static HotType LoadByType<T>()
        {
            return new HotType(typeof(T));
        }
        public static bool Is<T>(object obj, out T value)
        {
            if (obj is T o)
            {
                value = o;
                return true;
            }
            value = default;
            return false;
        }
        public bool IsEnum()
        {
            return this.IsType<Enum>();
        }
        public Tout CallByMethodId<Tout>(string id, params IHotParam[] _params)
        {
            return this.Methods.CallByMethodId<Tout>(null, id, _params);
        }
        public HotObject CallByMethodId(string id, params IHotParam[] _params)
        {
            return this.Methods.CallUnknownByMethodId(null, id, _params);
        }
        public Tout Call<Tout>(string name, params IHotParam[] _params)
        {
            return this.Methods.Call<Tout>(null, name, _params);
        }
        public HotObject Call(string name, params IHotParam[] _params)
        {
            return this.Methods.CallUnknown(null, name, _params);
        }
        public HotObject ReadById(string id)
        {
            return this.Members.GetUnknownByMemberId(null, id);
        }
        public Tout ReadById<Tout>(string id)
        {
            return this.Members.GetByMemberId<Tout>(null, id);
        }
        public HotObject Read(string name)
        {
            return this.Members.GetUnknown(null, name);
        }
        public Tout Read<Tout>(string name)
        {
            return this.Members.Get<Tout>(null, name);
        }
        public void Write<Tin>(string name, Tin value)
        {
            this.Members.Set(null, name, value);
        }
        public object NewObject(params IHotParam[] _params)
        {
            return NewObject(_type, _params);
        }
        public HotObject New(params IHotParam[] _params)
        {
            return new HotObject(this, NewObject(_params));
        }
        public T New<T>(params IHotParam[] _params)
        {
            if (NewObject(_params) is T a) return a;
            return default;
        }
        public static bool IsAbstract<T>()
        {
            return typeof(T).IsAbstract;
        }
        public static object NewObject(Type type, params IHotParam[] _params)
        {
            if (type.IsInterface || type.IsAbstract) throw new ArgumentException(type.Name); //不能new的场景
            return Activator.CreateInstance(type, HotParam.Read(_params));
        }
        public static T NewObject<T>(params IHotParam[] _params)
        {
            if (NewObject(typeof(T), _params) is T a) return a;
            return default;
        }
        public string FindEventId(string name)
        {
            return Events.FindEventId(name);
        }
        public void AddEventById(string id, params IHotEvent[] events)
        {
            this.Events.AddByEventId(null, id, events);
        }
        public void AddEvent(string name, params IHotEvent[] events)
        {
            this.Events.Add(null, name, events);
        }
        public void RemoveEventById(string id, params IHotEvent[] events)
        {
            this.Events.RemoveByEventId(null, id, events);
        }
        public void RemoveEvent(string name, params IHotEvent[] events)
        {
            this.Events.Remove(null, name, events);;
        }
        public HotObject InvokeEventById(string id, params IHotParam[] _params)
        {
            return this.Events.RasieUnknownByEventId(null, id, _params);
        }
        public Tout InvokeEventById<Tout>(string id, params IHotParam[] _params)
        {
            return this.Events.Rasie<Tout>(null, id, _params);
        }
        public HotObject InvokeEvent(string name, params IHotParam[] _params)
        {
            return this.Events.RasieUnknown(null, name, _params);
        }
        public Tout InvokeEvent<Tout>(string name, params IHotParam[] _params)
        {
            return this.Events.Rasie<Tout>(null, name, _params);
        }
        public IEnumerable<HotAttribute> FindAttributes()
        {
            foreach (var attr in Attributes.ReadAttributesByParentId(Id)) yield return attr;
        }
        public HotAttribute FindAttribute(string name)
        {
            var id = Attributes.FindAttributeId(Id, name);
            if (id == string.Empty) return null;
            return Attributes.ReadAttributeById(id);
        }
        public IEnumerable<HotAttribute> FindAttributes(string parentId)
        {
            foreach (var attr in Attributes.ReadAttributesByParentId(parentId)) yield return attr;
        }
        public HotAttribute FindAttribute(string parentId, string name)
        {
            var id = Attributes.FindAttributeId(parentId, name);
            if (id == string.Empty) return null;
            return Attributes.ReadAttributeById(id);
        }
        public IEnumerable<HotAttribute> FindFunctionAttributes(string functionname)
        {
            var id = Methods.FindMethodId(functionname);
            if (id != string.Empty) foreach (var attr in Attributes.ReadAttributesByParentId(id)) yield return attr;
        }
        public HotAttribute FindFunctionAttribute(string functionname, string name)
        {
            var parentId = Methods.FindMethodId(functionname);
            if (parentId == string.Empty) return null;
            var id = Attributes.FindAttributeId(parentId, name);
            if (id == string.Empty) return null;
            return Attributes.ReadAttributeById(id);
        }
        public IEnumerable<HotAttribute> FindItemAttributes(string itemname)
        {
            var id = Members.FindMemberId(itemname);
            if (id != string.Empty) foreach (var attr in Attributes.ReadAttributesByParentId(id)) yield return attr;
        }
        public HotAttribute FindItemAttribute(string itemname, string name)
        {
            var parentId = Members.FindMemberId(itemname);
            if (parentId == string.Empty) return null;
            var id = Attributes.FindAttributeId(parentId, name);
            if (id == string.Empty) return null;
            return Attributes.ReadAttributeById(id);
        }
        public IEnumerable<HotAttribute> FindEventAttributes(string itemname)
        {
            var id = Events.FindEventId(itemname);
            if (id != string.Empty) foreach (var attr in Attributes.ReadAttributesByParentId(id)) yield return attr;
        }
        public HotAttribute FindEventAttribute(string itemname, string name)
        {
            var parentId = Events.FindEventId(itemname);
            if (parentId == string.Empty) return null;
            var id = Attributes.FindAttributeId(parentId, name);
            if (id == string.Empty) return null;
            return Attributes.ReadAttributeById(id);
        }
        internal static bool IsType(Type check, string _typeName)
        {
            var typeName = _typeName.ToLower();
            var baseType = check;
            while (baseType != null)
            {
                if (baseType.Name.ToLower() == typeName) return true;
                baseType = baseType.BaseType;
            }
            foreach (var _interface in check.GetInterfaces()) if (_interface.Name.ToLower() == typeName) return true;
            return false;
        }
        public bool IsType(string _typeName)
        {
            return IsType(_type, _typeName);
        }
        public bool IsType(Type type)
        {
            return IsType(type.Name);
        }
        public bool IsType<T>()
        {
            return IsType(typeof(T));
        }
    }
}
